/*
* ====================================================================
* Copyright (c) 2013-2014 Neudesic.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that redistributions of source
* retain the above copyright notice, this list of conditions and the
* following disclaimer.
*
* This source file includes software written by Neudesic, LLC (http://www.neudesic.com).
*
* THIS SOFTWARE IS PROVIDED BY Neudesic, LLC ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  FURTHER, IN NO EVENT SHALL
* Neudesic, LLC (THE AUTHOR) OR ANY CONTRIBUTORS BE LIABLE FOR ANY DAMAGES
* WHATSOEVER THAT ARE RELATED TO THE SOFTWARE [INCLUDING BUT NOT LIMITED TO,
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* SUCH AS LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION, HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)] EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
*
* ====================================================================
*/
#include "StreamSocketContainer.h"
#include "SocketContainer.h"
#include "ISocketContainer.h"
#include <windows.system.threading.h>

using ABI::Windows::Foundation::AsyncStatus;
using ABI::Windows::Foundation::IAsyncAction;
using ABI::Windows::Foundation::IAsyncOperation;
using ABI::Windows::Foundation::IAsyncOperationCompletedHandler;
using ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler;
using ABI::Windows::Foundation::IAsyncOperationWithProgress;
using ABI::Windows::Foundation::IAsyncActionCompletedHandler;
using ABI::Windows::Foundation::ITypedEventHandler;
using ABI::Windows::Networking::IEndpointPair;
using ABI::Windows::Networking::Sockets::IStreamSocket;
using ABI::Windows::Networking::Sockets::IStreamSocketInformation;
using ABI::Windows::Storage::Streams::IBuffer;
using ABI::Windows::Storage::Streams::IBufferFactory;
using ABI::Windows::Storage::Streams::IDataReader;
//using ABI::Windows::Storage::Streams::IDataReaderFactory;
using ABI::Windows::Storage::Streams::IDataReaderStatics;
using ABI::Windows::Storage::Streams::IDataWriter;
using ABI::Windows::Storage::Streams::IInputStream;
using Microsoft::WRL::Callback;
using Microsoft::WRL::Wrappers::HString;
using Microsoft::WRL::Wrappers::HStringReference;

const int MAXBUFFER = 1024 * 1024;

namespace Neudesic {
	namespace Sockets {
		StreamSocketContainer::StreamSocketContainer()
			: SocketContainer(RuntimeClass_Windows_Networking_Sockets_StreamSocket, SocketType::StreamSocketType), receiveThreadStarted(false)
		{
			//::ShowInfoMessage(L"In StreamSocketContainer::c'tor for container [%d].", this);
		}

		StreamSocketContainer::~StreamSocketContainer(void)
		{
			//::ShowInfoMessage(L"In StreamSocketContainer::d'tor for container [%d] socket [%d].", this, this->GetSocket());
			AutoResetEvent wait;
			wait.Wait(500);
		}

		ISocketContainer* StreamSocketContainer::GetAcceptedSocket(struct sockaddr* addr, LPINT addrlen, LPCONDITIONPROC lpfnCondition, DWORD_PTR dwCallbackData)
		{
			return this;
		}

		HRESULT StreamSocketContainer::Listen(int backlog)
		{
			//The only thing needed here is to make certain that the socket has not been closed.
			HRESULT hr = this->IsRunning() ? S_OK : INVALID_SOCKET;
			::WSASetLastError(FAILED(hr) ? WSAEFAULT : 0);
			return hr;
		}

		void StreamSocketContainer::ReceiveThread(HANDLE enableReadEvent)
		{
			if (!this->receiveThreadStarted)
			{
				this->receiveThreadStarted = true;
				ComPtr<ABI::Windows::System::Threading::IThreadPoolStatics> threadPool;
				HRESULT hr = ABI::Windows::Foundation::GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPool).Get(), &threadPool);
				ComPtr<IAsyncAction> asyncAction;
				hr = threadPool->RunAsync(::Callback<ABI::Windows::System::Threading::IWorkItemHandler>(
					[this, enableReadEvent](IAsyncAction* asyncAction) -> HRESULT
				{
					//::ShowInfoMessage(L"Starting ReceiveThread with event handle [%d] for container [%d] socket [%d]", enableReadEvent, this, this->GetSocket());
					int result = this->WaitForEvent(enableReadEvent, TimeoutDuration::Infinity, L"ReceiveThreadWaitForEnable");
					if ((WAIT_OBJECT_0 + 1) == result)
					{
						while (true)
						{
							result = this->WaitForEvent(enableReadEvent, TimeoutDuration::Receive, L"ReceiveThreadLoop");
							if (WAIT_OBJECT_0 == result)	//Means we need to exit.
								break;
							//If the result is 1, raise an FD_READ.
							if ((WAIT_OBJECT_0 + 1) == result)
							{
								this->bufferProcessed.Reset();
								HRESULT hr = this->PrepareForMessages();
								::ContinueIfSucceeded(hr, L"PrepareForMessages failed for container [%d] socket [%d].", this, this->GetSocket());
								if (SUCCEEDED(hr))
									this->WaitForEvent(this->bufferProcessed.Get(), TimeoutDuration::Infinity, L"BufferProcessingWait");
							}
						}
					}
					//::ShowInfoMessage(L"Leaving ReceiveThread with event handle [%d] for container [%d] socket [%d]", enableReadEvent, this, this->GetSocket());
					return S_OK;
				}).Get(), &asyncAction);
			}
		}

		HRESULT StreamSocketContainer::ConnectSocket(const ComPtr<IHostName>& hostName, const wchar_t* serviceName)
		{
			if (!this->IsRunning())
				return INVALID_SOCKET;

			auto completionHandler = ::Callback<IAsyncActionCompletedHandler>(
				[this](IAsyncAction *asyncAction, AsyncStatus asyncStatus)
			{
				HRESULT completionResult = asyncAction->GetResults();
				switch (asyncStatus)
				{
				case AsyncStatus::Canceled:
				case AsyncStatus::Error:
					if (!FAILED(completionResult))
						completionResult = E_FAIL;
					break;
				case AsyncStatus::Completed:
					break;
				}
				::ContinueIfSucceeded(completionResult, L"Connect failure for container [%d] socket [%d]. AsyncStatus [%d].",
					this, this->GetSocket(), asyncStatus);
				//::ShowInfoMessage(L"ConnectAsync connection completed for container [%d] socket [%d].", this, this->GetSocket());
				//if (this->IsRunning())

#if USE_ASYNC_CONNECTANDCLOSE || USE_ASYNC_IO
				this->SignalConnectionComplete();
#else
				this->SetConnectComplete();
#endif
				return completionResult;
			});
			ComPtr<IAsyncAction> connectAsyncAction;
			HRESULT hr = this->GetSocket()->ConnectAsync(hostName.Get(), HStringReference(serviceName).Get(), &connectAsyncAction);
			if (::ContinueIfSucceeded(hr, L"Could not connect to remote service name for container [%d] socket [%d].", this, this->GetSocket()))
			{
				this->SetConnectAction(connectAsyncAction);
				hr = connectAsyncAction->put_Completed(completionHandler.Get());
				if (::ContinueIfSucceeded(hr, L"Call to put_Completed failed for container [%d] socket [%d].", this, this->GetSocket()))
				{
					//No need to wait here. We always check to make certain that the connect has complete.
					//#if !USE_ASYNC_IO && !USE_ASYNC_CONNECTANDCLOSE
					//#if _DEBUG
					//					hr = this->WaitForConnect(TimeoutDuration::Connect);
					//					if (SUCCEEDED(hr))
					//					{
					//						//Just a verification of what we IP and port we have connected to.
					//						sockaddr_storage sas;
					//						int len = sizeof(sockaddr_storage);
					//						hr = this->GetPeerName((sockaddr*)&sas, &len);
					//						if (SUCCEEDED(hr))
					//						{
					//							sockaddr_in* psa = (sockaddr_in*)&sas;
					//							char ip[NI_MAXHOST];
					//							::inet_ntop(psa->sin_family, (const void*)&psa->sin_addr, ip, sizeof(ip));
					//							char port[10];
					//							::sprintf_s(port, (sizeof(port) / sizeof(char)), "%d", ::tnet_ntohs(psa->sin_port));
					//							std::wstring wIP = ::ConvertToWideString(ip);
					//							std::wstring wPort = ::ConvertToWideString(port);
					//							HSTRING hCName;
					//							hostName->get_CanonicalName(&hCName);
					//							std::wstring hCanonicalName = ::ConvertToWideString(&hCName);
					//							::WindowsDeleteString(hCName);
					//							//These had better be the same...
					//							if (hCanonicalName != wIP || 0 != wPort.compare(serviceName))
					//							{
					//								::ShowErrorMessage(L"Connected to IP [%s] port [%d] family [%d] for container [%d] socket [%d]",
					//									wIP.c_str(), wPort.c_str(), psa->sin_family, this, this->GetSocket());
					//							}
					//						}
					//					}
					//#endif
					//#endif
				}
			}
			return hr;
		}

		HRESULT StreamSocketContainer::GetSocketName(struct sockaddr* name, int* namelen) const
		{
			if (!this->IsRunning())
				return INVALID_SOCKET;

			HRESULT hr = SOCKET_ERROR;
			//Like winsock, bind or connect must have been called. They will have saved the IP and port info.
			if (SocketContainerState::Connected == this->GetContainerState())
			{
				SocketInformation<IStreamSocket, IStreamSocketInformation> si(this->GetSocket());
				HostAndPortInformation info;
				si.GetLocalHostAndPortInfo(&info);
				////Is this an ANY bind address? If so, clear it. A bind should have already occured to a specific address.
				//if (info.host.compare(L"::") == 0 || info.host.compare(L"0.0.0.0") == 0)
				//	info.host.clear();

				//if (!info.host.length())
				//	info = this->GetBindHostAndPortInfo();
				//::ShowInfoMessage(L"GetSocketName::LocalCanonicalName [%s] LocalPort [%s] HostNameType [%d] for container [%d] socket [%d].",
				//	info.host.c_str(), info.port.c_str(), info.type, this, this->GetSocket());
				hr = this->SetSockAddrInCommon((void*)name, namelen, info.host, info.type, info.port);
			}
			::WSASetLastError(FAILED(hr) ? WSAEFAULT : 0);
			return hr;
		}

		HRESULT StreamSocketContainer::Send(const char* buf, unsigned int len, UINT32* numberOfBytesSent)
		{
			if (!this->IsRunning())
				return INVALID_SOCKET;
			HRESULT hr = this->WaitForConnect(TimeoutDuration::Connect);
			if (SUCCEEDED(hr))
			{
				this->enableReadEvent.Reset();
				//::ShowInfoMessage(L"Resetting event handle [%d] for container [%d] socket [%d]", this->enableReadEvent, this, this->GetSocket());
#if USE_ASYNC_IO
				auto sendCompletedHandler = ::Callback<IAsyncOperationWithProgressCompletedHandler<UINT32, UINT32>>(
					[this, numberOfBytesSent]
#else
				HANDLE hSendComplete = ::CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
				auto sendCompletedHandler = ::Callback<IAsyncOperationWithProgressCompletedHandler<UINT32, UINT32>>(
					[this, numberOfBytesSent, &hSendComplete]
#endif
				(IAsyncOperationWithProgress<UINT32, UINT32> *operation, AsyncStatus asyncStatus)
				{
					HRESULT sendCompletedResult = operation->GetResults(numberOfBytesSent);
					switch (asyncStatus)
					{
					case AsyncStatus::Canceled:
					case AsyncStatus::Error:
						if (!FAILED(sendCompletedResult))
							sendCompletedResult = E_FAIL;
						break;
					case AsyncStatus::Completed:
						//::ShowInfoMessage(L"Setting event handle [%d] for container [%d] socket [%d]", this->enableReadEvent, this, this->GetSocket());
						this->enableReadEvent.Set();
						::ContinueIfSucceeded(sendCompletedResult, L"Get number of bytes sent failed for container [%d] socket [%d].", this, this->GetSocket());
						break;
					}
					::ContinueIfSucceeded(sendCompletedResult, L"WriteAsync failure for container [%d] socket [%d]. AsyncStatus [%d].",
						this, this->GetSocket(), asyncStatus);
#if USE_ASYNC_IO
					this->SignalSendCompletion();
#else
					::SetEvent(hSendComplete);
#endif
					return sendCompletedResult;
				});
				ComPtr<IDataWriter> writer;
				hr = Windows::Foundation::ActivateInstance(HStringReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(), &writer);
				if (::ContinueIfSucceeded(hr, L"DataWriter ActivateInstance failed."))
				{
					hr = writer->WriteBytes(len, (BYTE*)buf);
					if (::ContinueIfSucceeded(hr, L"Write bytes failed."))
					{
						ComPtr<IBuffer> sendBuffer;
						hr = writer->DetachBuffer(&sendBuffer);
						if (::ContinueIfSucceeded(hr, L"Detach buffer failed."))
						{
							ComPtr<IOutputStream> outputStream;
							hr = this->GetSocket()->get_OutputStream(&outputStream);
							if (::ContinueIfSucceeded(hr, L"get_OutputStream failed."))
							{
								ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> action;
								hr = outputStream->WriteAsync(sendBuffer.Get(), &action);
								if (::ContinueIfSucceeded(hr, L"Write async failed."))
								{
									//this->SetSendAsyncOperation(action);
									hr = action->put_Completed(sendCompletedHandler.Get());
									if (::ContinueIfSucceeded(hr, L"Set OutputStream completion handler failed for socket [%d].", this))
									{
										//hr = this->PrepareForMessages();
										//::ContinueIfSucceeded(hr, L"PrepareForMessages call failed for container [%d] socket [%d].", this, this->GetSocket());
										this->ReceiveThread(this->enableReadEvent.Get());
#if !USE_ASYNC_IO
										int result = this->WaitForEvent(hSendComplete, TimeoutDuration::Send, L"SendComplete");
										if (WAIT_TIMEOUT == result)
										{
											::ShowErrorMessage(L"Timeout waiting for WriteAsync.");
											hr = HRESULT_FROM_WIN32(result);
										}
										//::ShowInfoMessage(L"Message sent for container [%d] socket [%d].", this, this->GetSocket());
#endif
									}
								}
							}
						}
					}
				}
#if !USE_ASYNC_IO
				::CloseHandle(hSendComplete);
#endif
			}
			::WSASetLastError(FAILED(hr) ? WSAEFAULT : S_OK);
			return hr;
		}

		HRESULT StreamSocketContainer::Send(const char* buf, unsigned int len, int flags, UINT32* numberOfBytesSent)
		{
			if (!this->IsRunning())
				return INVALID_SOCKET;
			// Hand it off to the socket to do the connect and send
			HRESULT hr = this->Send(buf, len, numberOfBytesSent);
			::WSASetLastError(FAILED(hr) ? WSAEFAULT : 0);
			return hr;
		}

		HRESULT StreamSocketContainer::Receive(char *buf, int len, int flags, LPDWORD lpNumberOfBytesRecvd)
		{
			if (!this->IsRunning())
				return INVALID_SOCKET;
			HRESULT hr = this->InternalReceive(buf, len, flags, lpNumberOfBytesRecvd);
			::WSASetLastError(FAILED(hr) ? WSAEFAULT : 0);
			return hr;
		}

		int StreamSocketContainer::Select(fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout)
		{
			//Convert structure to timeout in milliseconds.
			int timeInMs = timeout ? timeInMs = (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000) : 1000;

			int count = 0;
			StreamSocketContainer* container;
			if (writefds)
			{
				for (UINT i = 0; i < writefds->fd_count; i++)
				{
					container = reinterpret_cast<StreamSocketContainer*>(writefds->fd_array[i]);
					StreamSocketContainer::AdjustCount(container, &count, timeInMs);
				}
			}
			if (readfds)
			{
				for (UINT i = 0; i < readfds->fd_count; i++)
				{
					container = reinterpret_cast<StreamSocketContainer*>(readfds->fd_array[i]);
					StreamSocketContainer::AdjustCount(container, &count, timeInMs);
				}
			}

			return count;
		}

		void StreamSocketContainer::AdjustCount(StreamSocketContainer* container, int* count, int timeInMs)
		{
			if (container->IsRunning())
			{
				HRESULT hr = container->WaitForConnect(timeInMs);
				if (SUCCEEDED(hr))
				{
					(*count)++;
				}
			}
		}

		HRESULT StreamSocketContainer::GetUnconsumedBufferLength(long command, ULONG* argp)
		{
			HRESULT hr = S_OK;
			if (FIONREAD == command)
			{
				*argp = 0;
				int result = this->WaitForEvent(this->readComplete.Get(), TimeoutDuration::Receive, L"WaitForReadComplete");
				hr = (WAIT_TIMEOUT == result) ? HRESULT_FROM_WIN32(result) : S_OK;
				if (::ContinueIfSucceeded(hr, L"Waiting for ConnectComplete failed for container [%d] socket [%d].", this, this->GetSocket()))
				{
					if (!this->IsRunning())
						hr = INVALID_SOCKET;
				}
			}
			if (SUCCEEDED(hr))
				hr = SocketContainer::GetUnconsumedBufferLength(command, argp);
			return hr;
		}

		HRESULT StreamSocketContainer::InternalReceive(char *buf, int bufferLength, int flags, LPDWORD lpNumberOfBytesRecvd)
		{
			if (!this->IsRunning())
				return INVALID_SOCKET;
			this->WaitForEvent(this->readComplete.Get(), TimeoutDuration::Receive, L"WaitForReadComplete");
			if (!this->IsRunning())
				return INVALID_SOCKET;

			HRESULT hr = SocketContainer::InternalReceive(buf, bufferLength, flags, lpNumberOfBytesRecvd);
			ULONG numberOfBytesLeft;
			this->GetUnconsumedBufferLength(FIONREAD, &numberOfBytesLeft);
			if (!numberOfBytesLeft)
				this->bufferProcessed.Set();
			return hr;
		}

		HRESULT StreamSocketContainer::PrepareForMessages()
		{
			if (!this->IsRunning())
				return (HRESULT)INVALID_SOCKET;

			auto readCompletedHandler = ::Callback<IAsyncOperationWithProgressCompletedHandler<IBuffer*, UINT32>>(
				[this](IAsyncOperationWithProgress<IBuffer*, UINT32> *operation, AsyncStatus asyncStatus)
			{
				//::ShowInfoMessage(L"ReadAsync complete for container [%d] socket [%d]. AsyncStatus [%d].",
				//	this, this->GetSocket(), asyncStatus);
				HRESULT readCompletedResult = S_OK;
				switch (asyncStatus)
				{
				case AsyncStatus::Canceled:
				case AsyncStatus::Error:
					readCompletedResult = E_FAIL;
					break;
				case AsyncStatus::Completed:
					ComPtr<IBuffer> buffer;
					readCompletedResult = operation->GetResults(&buffer);
					::ContinueIfSucceeded(readCompletedResult, L"GetResults call for buffer failed for container [%d] socket [%d].",
						this, this->GetSocket());
					//UINT32 numberOfBytesInBuffer;
					//buffer->get_Length(&numberOfBytesInBuffer);
					//readCompletedResult = operation->GetResults(&this->numberOfBytesInBuffer);
					//::ShowInfoMessage(L"Number of bytes in buffer is %d for container [%d] socket [%d]. AsyncStatus [%d].",
					//	numberOfBytesInBuffer, this, this->GetSocket(), asyncStatus);
					readCompletedResult = this->PrepareDataReader(buffer);
					break;
				}
				::ContinueIfSucceeded(readCompletedResult, L"ReadAsync failure for container [%d] socket [%d]. AsyncStatus [%d].",
					this, this->GetSocket(), asyncStatus);
#if USE_ASYNC_IO
				this->SignalSendCompletion();
#else
				this->readComplete.Set();
#endif
				this->SetNetworkEvent(FD_READ, FD_READ_BIT, readCompletedResult);
				return readCompletedResult;
			});

			//::ShowInfoMessage(L"Received message for container [%d] socket [%d].", this, socket);
			ComPtr<IInputStream> inputStream;
			HRESULT hr = this->GetSocket()->get_InputStream(&inputStream);
			if (::ContinueIfSucceeded(hr, L"get_InputStream call failed for container [%d] socket [%d].", this, this->GetSocket()))
			{
				if (nullptr == this->receiveBuffer)
				{
					ComPtr<IBufferFactory> bufferFactory;
					hr = Windows::Foundation::GetActivationFactory(HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), &bufferFactory);
					if (::ContinueIfSucceeded(hr, L"GetActivationFactory for bufferFactory call failed for container [%d] socket [%d].", this, this->GetSocket()))
					{
						hr = bufferFactory->Create(MAXBUFFER, &this->receiveBuffer);
						::ContinueIfSucceeded(hr, L"Create buffer call failed for container [%d] socket [%d].", this, this->GetSocket());
					}
				}
				ComPtr<IAsyncOperationWithProgress<IBuffer*, UINT32>> readOperation;
				hr = inputStream->ReadAsync(this->receiveBuffer.Get(), MAXBUFFER, InputStreamOptions::InputStreamOptions_Partial, &readOperation);
				if (::ContinueIfSucceeded(hr, L"ReadAsync call failed for container [%d] socket [%d].", this, this->GetSocket()))
				{
					hr = readOperation->put_Completed(readCompletedHandler.Get());
					if (::ContinueIfSucceeded(hr, L"Read operation put_Completed call failed for container [%d] socket [%d].", this, this->GetSocket()))
					{
						//::ShowInfoMessage(L"Created DataReader for container [%d] socket [%d].", this, this->GetSocket());
						int result = this->WaitForEvent(this->readComplete.Get(), TimeoutDuration::Receive, L"WaitForReadAsync");
						if (WAIT_TIMEOUT == result)
						{
							::ShowErrorMessage(L"Timeout waiting for ReadAsync.");
							hr = HRESULT_FROM_WIN32(result);
						}
					}
				}
			}
			//}
			//#if USE_ASYNC_IO
			//			this->SignalReceiveComplete();
			//#else
			//this->SetNetworkEvent(FD_READ, FD_READ_BIT, 0);
			//#endif
			return hr;
		}

		HRESULT StreamSocketContainer::PrepareDataReader(ComPtr<IBuffer>& buffer)
		{
			if (!this->IsRunning())
				return (HRESULT)INVALID_SOCKET;

			ComPtr<IDataReaderStatics> dataReaderStatics;
			HRESULT hr = ABI::Windows::Foundation::GetActivationFactory(HStringReference(RuntimeClass_Windows_Storage_Streams_DataReader).Get(), &dataReaderStatics);
			ComPtr<IDataReader> reader;
			hr = dataReaderStatics->FromBuffer(buffer.Get(), &reader);

			if (::ContinueIfSucceeded(hr, L"Could not create DataReader from buffer for container [%d] socket [%d].", this, this->GetSocket()))
			{
				SocketContainer::PrepareDataReader(reader);
				::ContinueIfSucceeded(hr, L"PrepareDataReader call failed for container [%d] socket [%d].", this, this->GetSocket());
			}
			return hr;
		}

		HRESULT StreamSocketContainer::SetSocketOptions(int level, int optname, const char* optval, int optlen)
		{
			//At this time there are no options used by Doubango that requires setting control options.

			HRESULT hr = S_OK;
			//HRESULT hr = SOCKET_ERROR;
			//if (this->IsRunning())
			//{
			//	//ComPtr<IStreamSocketControl> control;
			//	//hr = this->GetSocket()->get_Control(&control);
			//}
			::WSASetLastError(FAILED(hr) ? WSAEFAULT : 0);
			return hr;
		}
	}
}